package ;
/**
 * ...
 * @author Mike
 */
import de.polygonal.ds.Graph;
import de.polygonal.ds.mem.DoubleMemory;
import de.polygonal.ds.mem.IntMemory;
import de.polygonal.ds.mem.MemoryAccess;
import de.polygonal.ds.mem.MemoryManager;
import flash.display.BitmapData;
import flash.geom.Vector3D;
import flash.Lib;
import flash.Memory;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.utils.ByteArray;
import flash.utils.CompressionAlgorithm;
import flash.Vector;
import haxe.Log;
import flash.events.Event;
import de.polygonal.ds.Array3;
import de.polygonal.ds.Array2;
import flash.errors.Error;

class Map 
{
	var p : Vector<Int>;
	var b : IntMemory; // 16 Mb of memory for map

	public static var _w : Int = 512;
	public static var _h : Int = 512;
	public static var _d : Int = 64;
	public static var PAGE_SIZE : Int = _d * _h;
	
	public function new( ) : Void {
		
		p  = new Vector<Int>();
		p.length = (_w * _h * _d );
		p.fixed = false;
		b = new IntMemory( _w * _h * 32 ); // 16 Mb of memory for map
		MemoryManager.malloc( b, b.bytes );	
	}

	public static inline function getRealIndex( x : Int, y : Int, z : Int ) : Int 
	{	
		if ( x < 0 || y < 0 || z < 0 || x > Map._w || y > Map._h || z > Map._d ) {
			trace( "OOB" + x +" " + y + " " +" "+ z );
			return -1;
		}else {
			// Address = Base + ((rowindex*col_size+colindex) * depth_size + depthindex) * Element_Size
			return ((y * 512 + x ) * 64 + z ) ;

		}
	}	
	public function loadmmap( mapData : ByteArray, len : Int, output: BitmapData ) : Void
	{
		Memory.select( mapData );
		
		// Get the first byte and bail if it doesn't look like an ( headerless ) AOS map 


		var mapFileCursor : Int  = 0;
		var x : Int, y: Int, z: Int =0;
		var iterationCount : Int = 0;
		var i : Int = 0;
		var color : UInt;
		var xdiv = 1;
		var ydiv = 1;

		output.lock();
		var start : Int = Lib.getTimer();
	
		for ( pix in 0...p.length){
			p[pix] = -1;
		}
		for ( y in 0..._h ) {
		  for ( x in 0..._w ) {
			 z = 0;
			 var top = true;
			 var savedCursor =0;
			 // Do this a number of times per column depending on how many spens 
			 // in the column
			 while ( mapFileCursor >= 0) {
				 
				
				i = 0;
				/*
				byte offset 	name 	meaning
				0 			N 	length of span data (N*4 bytes including span header)
				1 			S 	starting height of top colored run
				2 			E 	ending height of top colored run (length is E-S+1)
				3 			A 	starting height of air run (first span ignores value and assumes A=0)
				4 + i*4 	b 	blue color for colored voxel #i
				5 + i*4 	g 	green color for colored voxel #i
				6 + i*4 	r 	red color for colored voxel #i
				7 + i*4 	a 	alpha channel for colored voxel #i, actually used for shading in unknown way
				*/
				// Column and span info
				
				var number4ByteChunks : UInt = Memory.getByte( mapFileCursor );
				var topColorStart: UInt = Memory.getByte( mapFileCursor + 1 );
				var topColorEnd : UInt = Memory.getByte( mapFileCursor + 2 ); // inclusive
				var bottomColorStart: UInt;
				var bottomColorEnd: UInt; // exclusive
				var lenTop: UInt;
				var lenBottom: UInt;


				// Clear the sky
				
				for ( i in z...topColorStart )
				{	
					var oi = getRealIndex( x, y, i );
					p[oi] = -2;
				}
							
				// Colour in any blocks between the next span
				if ( top == true ) {
				

				Map.addPixel( 
							  output
							, Std.int( x )
							, Std.int( y )
							, mapFileCursor
							, 0
				);	
					top = false;
				}
				var it : UInt = 0;
				for ( z in topColorStart...topColorEnd +1) 
				{
					var oi = getRealIndex( x, y, z );
					p[oi] = mapFileCursor + 4 + (it*4);
					iterationCount += 1;
					it++;
				}
				savedCursor = mapFileCursor + 4 + (it * 4);
				
				lenBottom = topColorEnd - topColorStart+1 ;

				// infer the number of bottom colors in next span from chunk length
				lenTop = (number4ByteChunks-1) - lenBottom;
				
				// check for end of data marker
				if ( number4ByteChunks == 0) {
				   // infer ACTUAL number of 4-byte chunks from the length of the color data
				   mapFileCursor += 4 * (lenBottom + 1);
				   
				   break;
				}else{	
					
				// now skip the v pointer past the data to the beginning of the next span
				
				mapFileCursor +=  Memory.getByte( mapFileCursor ) * 4;
				
				bottomColorEnd   = Memory.getByte( mapFileCursor+3 ); // aka air start
				bottomColorStart = bottomColorEnd - lenTop;
				
				it = 1;
				for ( z in bottomColorStart...bottomColorEnd ) {
					var oi = getRealIndex( x, y, z );
					p[oi] = mapFileCursor + 4 + (it*4);
					iterationCount += 1;
					it++;
				}	
				}
			}	
		}
	   }
		Log.trace( "Inserted  " + iterationCount + " Nodes in " + ( Lib.getTimer() -start ) );


	
		var surfaces : Array<Int> = new Array<Int>();
		var solids : Array<Int> = new Array<Int>();
		var outOfBounds : Array<Int> = new Array<Int>();
		
		var n : Int = 0;
		

	start = Lib.getTimer();
	var osx : Int = 1;
	var osy : Int = 1;
	
	var cursor : Int = 0;
		for ( y in 0...512 ) {
			for ( x in 0...512 ) {	
				for ( z in 0...64 ) {
					
					// not air or solid
					if ( p[n] >0 ) {
/*	
 * 	//optimised away by setting all  voxels to -1 at the start
 *  // any voxels maeked -1 after first pass must be solid
 * */
								
/*							if ( 
									   isSurface( p, n + 1)
									|| isSurface( p, n - 1)
									|| isSurface( p, getRealIndex( x+1,y, z ) )
									|| isSurface( p, getRealIndex( x-1,y, z ) )
									|| isSurface( p, getRealIndex( x,y+1, z ) )
									|| isSurface( p, getRealIndex( x,y-1, z ) )
							) {*/
								//Is surface
								output.setPixel( cursor % output.width,  Std.int( cursor /output.height ) ,  getBGRA( p[n] ) );
								b.set( cursor++,   p[n] );
								
/*								Map.addPixel( 
															  output
															, ( x + Std.int( ( z / 8 ) % 16 )*512 ) 
															, ( y + Std.int( ( z / 8*8 ) % 8 )*512 )
															, p[n]
															, 0
												);*/
								

								
/*							} else {
								// Not surface
								
							}*/
							
					} else if (p[n] < 0) {
						solids.push( p[n] );
					}
					n++;
				}
			}
		}
		Memory.select( mapData );

		output.unlock(  );

		MemoryManager.free();

		Log.trace( "getRealIndex  0 0 0 = " + getRealIndex(0, 0, 0) );
		Log.trace( "getRealIndex  0 0 0 = " + getRealIndex(0, 0, -1) );	
		Log.trace( "getRealIndex  0 2 0 = " + getRealIndex(0, 1, 0) );
		Log.trace( "getRealIndex  0 0 1 = " + getRealIndex(0, 0, 1) );	
		
		Log.trace( "Hashed  " + n + "  Nodes in " + ( Lib.getTimer() -start ) );
		Log.trace( "Hashed  " + cursor + " surface Nodes " );
		Log.trace( "Hashed  " + p.length + " solid Nodes " );
		Log.trace( "Minimum bitmap size ( side )  " + pow2( Std.int(Math.sqrt(cursor)))  );

	}
	
	private static inline function isSurface( voxels : Vector<Int>, index: Int ): Bool 
	{
		var check : Int = 0;	
		// check above

		if ( index <0 || index >= Std.int( voxels.length) )
			return false;
		else
			if ( voxels[index] == -2  )
				return true;
			else	
				return false;

	}
	
	
	public static inline function addPixel( output:  BitmapData, xd : Int, yd :Int,  mem : Int, off : UInt ):Void 
	{
		
		output.setPixel( 
			  xd 
			, yd 
			, Map.getBGRA( 
				  mem
				,  off 
			) 
		);
	}

	public static inline function getBGRA( v : Int, sOffset : Int =0 ) : Int 
	{
		
		return (
			    ( Memory.getByte( v + sOffset) << 0 )  
			|   ( Memory.getByte( v + 1 + sOffset) << 8 ) 
			|   ( Memory.getByte( v + 2 + sOffset) << 16) 

		) ;	
	}
	public function pow2(val : Int) : Int {
		val--;
		val = (val >> 1) | val;
		val = (val >> 2) | val;
		val = (val >> 4) | val;
		val = (val >> 8) | val;
		val = (val >> 16) | val;
		val++; // Val is now the next highest power of 2.
 
	return val;
}	
}